package com.zenika.zentracker.spring;

import org.hibernate.HibernateException;
import org.hibernate.classic.Session;
import org.hibernate.engine.SessionFactoryImplementor;
import org.springframework.context.ApplicationContext;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.orm.hibernate3.SpringSessionContext;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import com.zenika.zentracker.hibernate.ZenTrackerSession;

public class ZenTrackerSpringSessionContext extends SpringSessionContext {

    private static final long serialVersionUID = 4539013817256633008L;

    private final SessionFactoryImplementor zenTrackerSessionFactory;
    private ApplicationContext applicationContext;
    
    public ZenTrackerSpringSessionContext(SessionFactoryImplementor sessionFactory) {
        super(sessionFactory);
        this.zenTrackerSessionFactory = sessionFactory;
    }

    /**
     * We need to redefine this method to make sure that we have a ZenTrackerSession instead of the real Hibernate Session.
     * 
     * The zenTracker Session is needed to be able to build statistics when calling the close() method.
     * 
     * Until we wrap this Session, we may miss monitoring information. It is not the case for ZenTracker 1.0 as we only
     * use for now interceptors and listeners for monitoring.
     */
    public Session currentSession() throws HibernateException {
        Session session = super.currentSession();
        
        SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(zenTrackerSessionFactory);
        
        if ((sessionHolder.containsSession(session)) 
                && !(ZenTrackerSession.class.isAssignableFrom(session.getClass()))) {

            sessionHolder.removeSession(session);
            ZenTrackerSession zenTrackerSession = new ZenTrackerSession(session, applicationContext);
            sessionHolder.addSession(zenTrackerSession);
            return zenTrackerSession;
        }
        return session;
    }

    public void setApplicationContext(ApplicationContext appContext) {
        this.applicationContext = appContext;
    }
 }
